/*
"The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is ReportSheet.

The Initial Developer of the Original Code is Amarnath Chatterjee.
Portions created by Amarnath Chatterjee are Copyright (C). All Rights Reserved.

Contributor(s): Amarnath Chatterjee.
*/

package Reporting
{
	import mx.controls.Alert;
	public class EquationSolver
	{
		
		public static const OPERATORS : RegExp = /\+|-|\*|\//;
		public static const CELL_REF_OPERATORS : RegExp = /cell\(|,|\)/;


		public function EquationSolver() 
		{
		}

		//If any of the tokens contains cell reference then replace them with their data.
		//In case any cell reference is not available in the cell value, then it will throw an error
		//because cell dependecies have to be resolved before equation solving.
		private function ResolveCellRef(etokens : Array, CellValues : Array) : void
		{
			for( var i : int = 0; i < etokens.length; i++)
			{
				var cellrefTokens : Array = etokens[i].split(CELL_REF_OPERATORS);
				if (cellrefTokens.length == 4)
				{
					var r : int = parseInt(cellrefTokens[1]);
					var c : int = parseInt(cellrefTokens[2]);
					
					var found : Boolean = false;
					for( var j : int = 0; j < CellValues.length; j++)
					{
						if((CellValues[j].row == r) && (CellValues[j].col == c))
						{
							etokens[i] = CellValues[j].data;
							found = true;
							break;
						}	
					}
					
					if(!found)
						throw new Error("Cell Dependencies are not resolved.");
				}
			}	
		}
		
		//This function parses the expression and returns the immediate level required cells.
		public function GetRequiredCells( exprn : String ) : Array
		{
			var retArray : Array = new Array();
			var etokens: Array = exprn.split(OPERATORS);
			for( var i : int = 0; i < etokens.length; i++)
			{
				var cellrefTokens : Array = etokens[i].split(CELL_REF_OPERATORS);
				if (cellrefTokens.length == 4)
				{
					var r : int = parseInt(cellrefTokens[1]);
					var c : int = parseInt(cellrefTokens[2]);
					var ci : CellInfo = new CellInfo();
					ci.row = r;
					ci.col = c;
					retArray.push(ci);
				}
			}
			return retArray;	
		}
		 
		//This function solves the expression. It uses CellValues to resolve cell references.
		//All cell ref are resolved first.
		//* and / are of highest priority
		//+ and - are next
		public function Solve(exprn : String, CellValues : Array) : String
		{	
			//Split into tokens and operators
			var etokens: Array = exprn.split(OPERATORS);
			var eoperators : Array = new Array();
			var etokenStart : int = 0,  etokenEnd : int = 0;
			for( var temp : int = 0; temp < etokens.length-1; temp++)
			{
				etokenStart = exprn.indexOf(etokens[temp], etokenEnd);
				etokenEnd = etokenStart + etokens[temp].length;
				eoperators.push(exprn.charAt(etokenEnd));	
			}
			
			var operandStack : EquStack = new EquStack();
			var operatorStack : EquStack = new EquStack();
			
			var operand2 : Number;
			var operand1 : Number;
			var result : Number;
			var nextOperator : String;
			
			//First resolve all the references.
			ResolveCellRef(etokens, CellValues);
			
			//first process multiplication and division.
			//In case of negetive operator, we should convert it into +-. Like 5-6 as 5+-6
			operandStack.Push(etokens[0]);
			for( var etokenCtr : int = 1, eoprCtr : int = 0 ; etokenCtr < etokens.length; etokenCtr++)
			{ 
				nextOperator = eoperators[eoprCtr++];
				operandStack.Push(etokens[etokenCtr]);
				
				if(nextOperator == "*" || nextOperator == "/")
				{
					 operand2 = parseFloat(operandStack.Pop());
					 operand1 = parseFloat(operandStack.Pop());
					
					
					if(nextOperator == "*")
						result = operand1 * operand2;
					else
						result = operand1 / operand2;
					
					operandStack.Push(result.toString());
				}
				else
				{
					//Converting a - operator to +-. Popping the last operand.
					if(nextOperator == "-")
					{
						nextOperator = "+";
						
						//Chances are unlikely, but still.
						if(!operandStack.IsEmpty())
						{
							var convOperand : String = operandStack.Pop();
							operandStack.Push("-" + convOperand);
						}
					}
					operatorStack.Push(nextOperator);
				}				
			}
			
			//Finally do only addition.

			while(!operatorStack.IsEmpty())
			{
				operand2 = parseFloat(operandStack.Pop());
				operand1 = parseFloat(operandStack.Pop());
				nextOperator = operatorStack.Pop();
				result = 0;
				
				if(nextOperator == "+")
				{
					result = operand1 + operand2;
				}
				
				operandStack.Push(result.toString());		
			}
			
			//The last element present in operandStack is the final result.
			return operandStack.Pop().toString();		
		}
	}
}

class EquStack
{
	private var m_store : Array = new Array();
	private var m_ctr : int = 0;
	
	public function Push(elem : String) : void
	{
		m_store[m_ctr++] = elem;
	}
	public function Pop() : String
	{
		if(m_ctr == 0)
			return "";
		
		return m_store[--m_ctr];
	}
	public function IsEmpty() : Boolean
	{
		return m_ctr == 0;
	}
	
	public function toString() : String
	{
		return m_store.toString();
	}	
}